package phone

import (
	"bytes"
	"errors"
	"os"
	"regexp"
)

const (
	CMCC               byte = iota + 0x01 //中国移动
	CUCC                                  //中国联通
	CTCC                                  //中国电信
	CTCC_v                                //电信虚拟运营商
	CUCC_v                                //联通虚拟运营商
	CMCC_v                                //移动虚拟运营商
	INT_LEN            = 4
	CHAR_LEN           = 1
	HEAD_LENGTH        = 8
	PHONE_INDEX_LENGTH = 9
)

type PhoneRecord struct {
	V手机号 string
	V省   string
	V城市  string
	V邮编  string
	V区   string
	V运营商 string
}

var (
	//content     []byte
	CardTypemap = map[byte]string{
		CMCC:   "中国移动",
		CUCC:   "中国联通",
		CTCC:   "中国电信",
		CTCC_v: "中国电信虚拟运营商",
		CUCC_v: "中国联通虚拟运营商",
		CMCC_v: "中国移动虚拟运营商",
	}
	//total_len, firstoffset int32
)

type S归属地 struct {
	TotalLen, Firstoffset int32
	Content               []byte
}

func F新建归属地查询(path string) (s *S归属地, err error) {
	s = new(S归属地)
	s.Content, err = os.ReadFile(path)
	if err != nil {
		return
	}
	s.TotalLen = int32(len(s.Content))
	s.Firstoffset = get4(s.Content[INT_LEN : INT_LEN*2])
	return
}

func get4(b []byte) int32 {
	if len(b) < 4 {
		return 0
	}
	return int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24
}

func getN(s string) (uint32, error) {
	var n, cutoff, maxVal uint32
	i := 0
	base := 10
	cutoff = (1<<32-1)/10 + 1
	maxVal = 1<<uint(32) - 1
	for ; i < len(s); i++ {
		var v byte
		d := s[i]
		switch {
		case '0' <= d && d <= '9':
			v = d - '0'
		case 'a' <= d && d <= 'z':
			v = d - 'a' + 10
		case 'A' <= d && d <= 'Z':
			v = d - 'A' + 10
		default:
			return 0, errors.New("invalid syntax")
		}
		if v >= byte(base) {
			return 0, errors.New("invalid syntax")
		}

		if n >= cutoff {
			// n*base overflows
			n = (1<<32 - 1)
			return n, errors.New("value out of range")
		}
		n *= uint32(base)

		n1 := n + uint32(v)
		if n1 < n || n1 > maxVal {
			// n+v overflows
			n = (1<<32 - 1)
			return n, errors.New("value out of range")
		}
		n = n1
	}
	return n, nil
}

// 二分法查询phone数据
func (a *S归属地) F查找归属地(phone_num string) (pr *PhoneRecord, err error) {
	if a.Content == nil {
		err = errors.New("手机数据文件不存在")
		return
	}
	if len(phone_num) < 7 || len(phone_num) > 11 {
		return nil, errors.New("illegal phone length")
	}

	var left int32
	phone_seven_int, err := getN(phone_num[0:7])
	if err != nil {
		return nil, errors.New("illegal phone number")
	}
	phone_seven_int32 := int32(phone_seven_int)
	right := (a.TotalLen - a.Firstoffset) / PHONE_INDEX_LENGTH
	for {
		if left > right {
			break
		}
		mid := (left + right) / 2
		offset := a.Firstoffset + mid*PHONE_INDEX_LENGTH
		if offset >= a.TotalLen {
			break
		}
		cur_phone := get4(a.Content[offset : offset+INT_LEN])
		record_offset := get4(a.Content[offset+INT_LEN : offset+INT_LEN*2])
		card_type := a.Content[offset+INT_LEN*2 : offset+INT_LEN*2+CHAR_LEN][0]
		switch {
		case cur_phone > phone_seven_int32:
			right = mid - 1
		case cur_phone < phone_seven_int32:
			left = mid + 1
		default:
			cbyte := a.Content[record_offset:]
			end_offset := int32(bytes.Index(cbyte, []byte("\000")))
			data := bytes.Split(cbyte[:end_offset], []byte("|"))
			card_str, ok := CardTypemap[card_type]
			if !ok {
				card_str = "未知电信运营商"
			}
			pr = &PhoneRecord{
				V手机号: phone_num,
				V省:   string(data[0]),
				V城市:  string(data[1]),
				V邮编:  string(data[2]),
				V区:   string(data[3]),
				V运营商: card_str,
			}
			return
		}
	}
	return nil, errors.New("phone's data not found")
}

// 检查手机号是否有效
func F检测是否为手机号(V手机号ber string) bool {
	pattern := `^1[3-9]\d{9}$`
	reg := regexp.MustCompile(pattern)
	return reg.MatchString(V手机号ber)
}
